package cn.demoncat.util.mongo.util;

import cn.demoncat.util.exception.ParamRuntimeException;
import cn.demoncat.util.lang.MapUtil;
import cn.demoncat.util.lang.ParamEmptyUtil;
import cn.demoncat.util.lang.StringUtil;
import cn.demoncat.util.lang.constant.StringConstant;
import cn.demoncat.util.lang.entity.PageParam;
import cn.demoncat.util.lang.entity.Pager;
import cn.demoncat.util.mongo.constant.MongoConstant;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.NullHandling;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.core.query.*;

import java.util.*;
import java.util.Map.Entry;

/**
 * 描述：MongoDB查询工具（获取查询参数Query）
 *
 * @author 延晓磊
 *
 * @since 2017年7月22日
 */
public class QueryUtil {

	/**
	 * 使用MongoDB的查询语句创建Query
	 * 
	 * @param json	JSON格式的查询语句，如"{title:'MongoDB', age:{$lt:50}, balance:{$gt:10.00}}"
	 * 
	 * @return 
	 *
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Query build(String json) {
		if (StringUtils.isBlank(json)) {
			return build();
		}
		return new BasicQuery(json);
	}
	
	/**
	 * 使用MongoDB的查询语句创建Query
	 * 
	 * @param jsonQuery 	查询条件命令：{name:"a",age:{$lt:50}}
	 * @param jsonFilter 	字段过滤命令：{name:1 显示,age:0 过滤}	
	 * @return
	 *
	 * @author 延晓磊
	 */
	public static Query build(String jsonQuery, String jsonFilter) {
		return new BasicQuery(jsonQuery, jsonFilter);
	}
	
	/**
	 * 空查询
	 * 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Query build() {
		return new Query();
	}
	
	/**
	 * 条件查询
	 * 
	 * @param cri	条件
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Query build(Criteria cri) {
		return Query.query(cri);
	}
	
	/**
	 * 条件查询
	 * 
	 * @param o	查询对象（匹配非空属性）
	 * 
	 * @return 
	 *
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static <T> Query build(T o) {
		if (o == null) {
			return build();
		}
		// db.集合名.find({key1:"val1",key2:"val2"})
		return build(CriteriaUtil.by(o));
	}
	
	/**
	 * 分页 + 排序
	 * 
	 * @param query 	查询参数
	 * @param page 		分页条件 {@link QueryUtil#getPageRequest}
	 * @param sort 		排序条件 {@link QueryUtil#getSort}
	 * 
	 * @return 
	 *
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Query build(Query query, PageRequest page, Sort sort) {
		// db.集合名.find().sort({title:1 , by:-1}).skip((2-1)*10).limit(10)
		return query.with(sort).with(page);
	}
	
	/**
	 * 分页
	 * 
	 * @param query 	查询参数
	 * @param page 		分页条件 {@link QueryUtil#getPageRequest}
	 * 
	 * @return 
	 *
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Query build(Query query, PageRequest page) {
		// db.集合名.find().skip((page-1)*size).limit(size)
		return query.with(page);
	}

	/**
	 * 排序
	 * 
	 * @param query 	查询参数
	 * @param sort 		排序条件 {@link QueryUtil#getSort}
	 * 
	 * @return 
	 *
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Query build(Query query, Sort sort) {
		// db.集合名.find().sort( {title:1 , by:-1} )
		return query.with(sort);
	}

	/**
	 * 叠加条件
	 * 
	 * @param query	查询参数
	 * @param cri	叠加条件
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Query add(Query query, Criteria cri) {
		return query.addCriteria(cri);
	}
	
	/**
	 * 排除域
	 * 
	 * @param query		查询参数
	 * @param fields	排除的域
	 * 
	 * @return 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Query exclude(Query query, String... fields) {
		Field qfs = query.fields();
		for (String field : fields) {
			qfs.exclude(field);
		}
		return query;
	}
	
	/**
	 * 包含域
	 * 
	 * @param query		查询参数
	 * @param fields	包含的域
	 * 
	 * @return 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Query include(Query query, String... fields) {
		Field qfs = query.fields();
		for (String field : fields) {
			qfs.include(field);
		}
		return query;
	}
	
	/**
	 * 修剪域
	 * 
	 * @param query		查询参数
	 * @param field		剪切的域
	 * @param size		保留长度
	 * 
	 * @return 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Query slice(Query query, String field, int size) {
		query.fields().slice(field, size);
		return query;
	}
	
	/**
	 * 限制结果
	 * 
	 * @param query	查询参数
	 * @param skip	跳过数
	 * @param limit	限定数
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Query limit(Query query, long skip, int limit) {
		return query.skip(skip).limit(limit);
	}
	
	/**
	 * 限制结果
	 * 
	 * @param query	查询参数
	 * @param limit	限定数
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Query limit(Query query, int limit) {
		return query.limit(limit);
	}
	
	/**
	 * 限制结果
	 * 
	 * @param query	查询参数
	 * @param skip	跳过数
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static Query skip(Query query, long skip) {
		return query.skip(skip);
	}
	
	/**
	 * ID查询
	 * 
	 * @param id
	 * 
	 * @return 
	 *
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Query idQuery(Object id) {
		ParamEmptyUtil.checkNull(id);
		// db.集合名.find({_id:11})
		return build(CriteriaUtil.id(id));
	}

	/**
	 * IDS查询
	 * 
	 * @param ids
	 * 
	 * @return 
	 *
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static <P>Query idsQuery(Collection<P> ids) {
		if (CollectionUtils.isEmpty(ids)) {
			throw ParamRuntimeException.NULL;
		}
		// {_id :{$in : [9,10]}
		return build(CriteriaUtil.in(MongoConstant.ID_FIELD, ids.toArray()));
	}

	/**
	 * IDS查询
	 * 
	 * @param ids
	 * 
	 * @return 
	 *
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static Query idsQuery(Object... ids) {
		if (ArrayUtils.isEmpty(ids)) {
			throw ParamRuntimeException.NULL;
		}
		// {id :{$in : [9,10]}
		return build(CriteriaUtil.in(MongoConstant.ID_FIELD, ids));
	}

	/**
	 * 域值查询
	 * 
	 * @param field 域名
	 * @param value 域值
	 * 
	 * @return 
	 *
	 * @author 延晓磊
	 */
	public static Query kvQuery(String field, Object value) {
		return build(CriteriaUtil.is(field, value));
	}

	/**
	 * 域值对查询
	 * 
	 * @param kvs 域值对
	 * 
	 * @return 
	 *
	 * @author 延晓磊
	 */
	public static Query kvsQuery(Map<String, Object> kvs) {
		if (MapUtil.isEmpty(kvs)) {
			return build();
		}
		return build(CriteriaUtil.is(kvs));
	}

	/**
	 * 正则查询
	 * 
	 * @param field 域名
	 * @param regex 域值正则
	 * 
	 * @return 
	 *
	 * @author 延晓磊
	 */
	public static Query regexQuery(String field, String regex) {
		return build(CriteriaUtil.regex(field, regex));
	}
	
	/**
	 * 附近查询（平面）
	 * 
	 * @param query		查询条件
	 * @param x			基准位置
	 * @param y
	 * @param max		最远距离（KM）
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static NearQuery nearQuery(Query query, double x, double y, double max) {
		return NearQuery.near(new Point(x, y)).maxDistance(new Distance(max, Metrics.KILOMETERS)).query(query);
	}
	
	/**
	 * 附近查询（平面）
	 * 
	 * @param query		查询条件
	 * @param x			基准位置
	 * @param y
	 * @param min		最近距离（KM）
	 * @param max		最远距离（KM）
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static NearQuery nearQuery(Query query, double x, double y, double min, double max) {
		return NearQuery.near(new Point(x, y), Metrics.KILOMETERS).minDistance(min).maxDistance(max).query(query);
	}
	
	/**
	 * 附近查询（球面）
	 * 
	 * @param query		查询条件
	 * @param x			基准位置
	 * @param y
	 * @param max		最远距离（KM）
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static NearQuery nearSphericalQuery(Query query, double x, double y, double max) {
		return NearQuery.near(new Point(x, y)).maxDistance(new Distance(max, Metrics.KILOMETERS)).query(query).spherical(true);
	}
	
	/**
	 * 附近查询（球面）
	 * 
	 * @param query		查询条件
	 * @param x			基准位置
	 * @param y
	 * @param min		最近距离（KM）
	 * @param max		最远距离（KM）
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年8月28日
	 */
	public static NearQuery nearSphericalQuery(Query query, double x, double y, double min, double max) {
		return NearQuery.near(new Point(x, y), Metrics.KILOMETERS).minDistance(min).maxDistance(max).query(query).spherical(true);
	}
	
	/**
	 * 排序方式
	 * 
	 * @param field 		排序的字段
	 * @param isAsc 		是否升序
	 * @param isNullLast 	是否Null排序在后
	 * 
	 * @return
	 */
	public static Order getOrder(String field, boolean isAsc, boolean isNullLast) {
		Direction direct = isAsc ? Direction.ASC : Direction.DESC;
		NullHandling nullHand = isNullLast ? NullHandling.NULLS_LAST : NullHandling.NULLS_FIRST;
		return new Order(direct, field, nullHand);
	}
	
	/**
	 * 排序参数
	 * 
	 * @param field
	 * @param isAsc
	 * @param isNullLast
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-3
	 */
	public static Sort getSort(String field, boolean isAsc, boolean isNullLast) {
		return Sort.by(getOrder(field, isAsc, isNullLast));
	}
	
	/**
	 * 排序参数
	 * 
	 * @param field
	 * @param isAsc
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-3
	 */
	public static Sort getSort(String field, boolean isAsc) {
		return getSort(field, isAsc, true);
	}
	
	/**
	 * 排序参数
	 * 
	 * @param sorts 排序方式 {字段名:0升序/1逆序}
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-1
	 */
	public static Sort getSort(LinkedHashMap<String, Integer> sorts) {
		if (MapUtil.isEmpty(sorts)) {
			return null;
		}
		// 转换
		List<Order> orders = new ArrayList<>();
		for (Entry<String, Integer> s : sorts.entrySet()) {
			orders.add(getOrder(s.getKey(), s.getValue() == 0, true));
		}
		// 转换为Sort
		return Sort.by(orders);
	}
	
	/**
	 * 
	 * 根据SQL创建排序参数
	 *
	 * @param sqlSort	SQL命令：xx ASC,yy DESC 或 xx,yy DESC格式
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年6月9日
	 */
	public static Sort getSort(String sqlSort) {
		if (StringUtils.isBlank(sqlSort)) {
			return null;
		}
		// 切分多个排序
		String[] sorts = StringUtil.splitStrict(sqlSort);
		String[] sort;
		List<Order> orders = new ArrayList<>();
		for (String s : sorts) {
			// 切分排序
			sort = s.trim().split(StringConstant.SPACING);
			orders.add(getOrder(sort[0], sort.length == 1 || sort[1].equals("ASC"), true));
		}
		return Sort.by(orders);
	}
	
	/**
	 * ID排序
	 * 
	 * @param isAsc
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020-9-1
	 */
	public static Sort getSort(boolean isAsc) {
		Sort sort = Sort.by(MongoConstant.ID_FIELD);
		return isAsc ? sort.ascending() : sort.descending();
	}
	
	/**
	 * 创建分页参数
	 *
	 * @param pageNum
	 * @param pageSize
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static PageRequest getPageRequest(int pageNum, int pageSize) {
		//初始化分页参数
		PageParam pager = PageParam.build(pageNum, pageSize);
		// db.集合名.find().skip((page-1)*size).limit(size)
		return PageRequest.of(pager.getPageNum() - 1, pager.getPageSize());
	}
	
	/**
	 * 创建分页参数
	 *
	 * @param pager
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年7月22日
	 */
	public static PageRequest getPageRequest(Pager<?> pager) {
		// db.集合名.find().skip((page-1)*size).limit(size)
		return PageRequest.of(pager.getPageNum() - 1, pager.getPageSize());
	}

	
}
